//
//  KImage.m
//  ToneMapper
//
//  Created by Andrey on 22/03/2009.
//  Copyright 2009 Karma Software. All rights reserved.
//

#import "KImage.h"
#import "KPixel.h"
#import "KHistogram.h"
#import "KCumulativeHistogram.h"
#import "K2DHistrogram.h"
#import "KImageLayer.h"

@implementation KImage

@synthesize width;
@synthesize height;
@synthesize indexOfSelectedLayer;
@synthesize histogram;
@synthesize cumulativeHistogram;
@synthesize cieABHistogram;

- (id) init
{
	if (self = [super init])
	{
		width = height = 0;
		
		layers = [[NSMutableArray arrayWithCapacity:1] retain];
		cachedRenderBitmap = nil;		
		
		needRecomputeHistogram = YES;
		needRenderBitmap = YES;
		histogram = 
		[[[KHistogram alloc] initWithIntervalCount:256 
								   andChannelCount: IMAGE_CHANNEL_COUNT] retain];
		
		cumulativeHistogram = 
		[[[KCumulativeHistogram alloc] initWithIntervalCount:256 
											 andChannelCount: IMAGE_CHANNEL_COUNT] retain];
		
		cieABHistogram =
		[[[K2DHistrogram alloc] initWithIntervalCount:(256 * 256) andChannelCount:1] retain];
	}
	
	return self;
}

- (id) initWithWidth: (NSUInteger)aWidth andHeight: (NSUInteger)aHeight
{
	if (self = [super init])
	{
		width = aWidth;
		height = aHeight;
		
		layers = [[NSMutableArray arrayWithCapacity:1] retain];
		
		KImageLayer* backgroundLayer = 
		[[KImageLayer alloc] initWithWidth:aWidth andHeight:aHeight];
		[layers addObject:backgroundLayer];
		
		cachedRenderBitmap = 
		[[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL 
												pixelsWide:width 
												pixelsHigh:height
											 bitsPerSample:8
										   samplesPerPixel:3
												  hasAlpha:NO
												  isPlanar:NO
											colorSpaceName:NSCalibratedRGBColorSpace
											   bytesPerRow:0
											  bitsPerPixel:0] retain];
		
		needRecomputeHistogram = YES;
		needRenderBitmap = YES;
		histogram = 
		[[[KHistogram alloc] initWithIntervalCount:256 
								   andChannelCount: IMAGE_CHANNEL_COUNT] retain];
		
		cumulativeHistogram = 
		[[[KCumulativeHistogram alloc] initWithIntervalCount:256 
											 andChannelCount: IMAGE_CHANNEL_COUNT] retain];
		
		cieABHistogram =
		[[[K2DHistrogram alloc] initWithIntervalCount:(256 * 256) andChannelCount:1] retain];
	}
	
	return self;
}

- (void) loadImageFromFile: (NSString*)filename reallocatingPixels: (Boolean)reallocate
{
	NSString* urlPrefix = @"file://";
	NSString* urlFromFilename = [urlPrefix stringByAppendingString:filename];
	NSBitmapImageRep* fileContents = 
	[NSBitmapImageRep imageRepWithContentsOfURL: [NSURL URLWithString:urlFromFilename]];
	
	if (reallocate == YES)
	{
		width = [fileContents pixelsWide];
		height = [fileContents pixelsHigh];

		[layers removeAllObjects];
		layers = [[NSMutableArray arrayWithCapacity:1] retain];
		
		KImageLayer* backgroundLayer = 
		[[KImageLayer alloc] initWithWidth:width andHeight:height];
		[layers addObject:backgroundLayer];
	}
	
	KImageLayer* backgroundLayer = [layers objectAtIndex:0];
	ForEachImagePixel(self)
	{
		KPixel* pixel = [[KPixel alloc] initWithNSColor:[fileContents colorAtX:x y:y]];
		[backgroundLayer setPixel:pixel atX:x y:y];
	}
}

- (id) initWithFile: (NSString*)filename
{
	if (self = [super init])
	{
		[self loadImageFromFile:filename reallocatingPixels: YES];
		
		cachedRenderBitmap = 
		[[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL 
												pixelsWide:width 
												pixelsHigh:height
											 bitsPerSample:8
										   samplesPerPixel:3
												  hasAlpha:NO
												  isPlanar:NO
											colorSpaceName:NSCalibratedRGBColorSpace
											   bytesPerRow:0
											  bitsPerPixel:0] retain];
		
		needRecomputeHistogram = YES;
		needRenderBitmap = YES;
		histogram = [[[KHistogram alloc] initWithIntervalCount:256 
											   andChannelCount: IMAGE_CHANNEL_COUNT] retain];
		cumulativeHistogram = 
		[[[KCumulativeHistogram alloc] initWithIntervalCount:256 
											 andChannelCount: IMAGE_CHANNEL_COUNT] retain];

		cieABHistogram =
		[[[K2DHistrogram alloc] initWithIntervalCount:(256 * 256) andChannelCount:1] retain];
		
		[histogram computeForImage:self];
		[cumulativeHistogram computeForImage:self];
		[cieABHistogram computeForImage:self];
	}
	
	return self;
}

- (void) addLayerOnTop: (KImageLayer*)newLayer
{
	if (newLayer != nil)
		[layers addObject:newLayer];
}

- (void) insertLayer: (KImageLayer*)newLayer atIndex: (NSUInteger)index
{
	if (newLayer != nil)
		[layers insertObject:newLayer atIndex:index];
}

- (KImageLayer*) layerAtIndex: (NSUInteger)index
{
	return [layers objectAtIndex:index];
}

- (void) removeLayerFromTop
{
	[layers removeLastObject];
}

- (void) removeLayerAtIndex: (NSUInteger)index
{
	[layers removeObjectAtIndex:index];
}

- (KPixel*) pixelAtX: (NSUInteger)x y: (NSUInteger)y
{
	KImageLayer* selectedLayer = [layers objectAtIndex:indexOfSelectedLayer];
	
	return [selectedLayer pixelAtX:x y:y];
}

- (void) setPixel: (KPixel*)pixel atX: (NSUInteger)x y: (NSUInteger)y
{
	KImageLayer* selectedLayer = [layers objectAtIndex:indexOfSelectedLayer];
	
	[selectedLayer setPixel:pixel atX:x y:y];
	
	needRenderBitmap = YES;
}

- (NSBitmapImageRep*) bitmap
{
	if (needRenderBitmap == YES)
	{
		[self renderToBitmap];
		needRenderBitmap = NO;
	}

	return cachedRenderBitmap;
}

- (void) renderToBitmap
{	
	ForEachImagePixel(self)
	{
		KPixel* currentPixel = [[layers objectAtIndex:0] pixelAtX:x y:y];
		
		for (int i = [layers count] - 1; i >= 1; i++)
		{
			KImageLayer* currentLayer = [layers objectAtIndex:i];
			currentPixel = [currentLayer pixelAtX:x y:y];
			
			KPixel* underlyingPixel = 
			[[layers objectAtIndex:i - 1] pixelAtX:x y:y];
			
			currentPixel = [currentPixel blendedPixelWithFraction:currentLayer.opacity 
														  ofPixel:underlyingPixel 
												   usingBlendMode:currentLayer.blendMode];
		}
		
		NSColor* currentPixelColor = [currentPixel nsColor];
		[cachedRenderBitmap setColor:currentPixelColor atX:x y:y];
	}
}

- (void) deallocInternal
{
	[cumulativeHistogram release];
	[histogram release];
	[cachedRenderBitmap release];
	[layers removeAllObjects];
}

- (void) restoreFromBackup: (NSString*)backupFilename
{
	[self loadImageFromFile:backupFilename reallocatingPixels:NO];
	needRenderBitmap = YES;
}

- (void) dealloc
{
	[self deallocInternal];
	[super dealloc];
}
@end
